/**
 * @team Dynamic Duo <dynamic.duo.compilation.team@gmail.com>
 * 1. Maayan Keshet
 * 2. Gal Bitensky
 * 
 *
 */

/*
 * 
 ____  _  _  __ _   __   _  _  __  ___    ____  _  _   __  
 (    \( \/ )(  ( \ / _\ ( \/ )(  )/ __)  (    \/ )( \ /  \ 
 ) D ( )  / /    //    \/ \/ \ )(( (__    ) D () \/ ((  O )
 (____/(__/  \_)__)\_/\_/\_)(_/(__)\___)  (____/\____/ \__/ 
 * 
 * 
 */

import ic.LanguageException;
import ic.ast.Node;

import java.io.File;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import lex.Lexer;
import lex.Token;
import parser.Parser;
import semantics.ScopeFiller;
import semantics.TypeAndRefChecker;
import tacGen.TacVisitor;

public class Main {
	private static final String DEFAULT_LIB_FILE = "libic.sig";

	public static void main(String[] args) {
		try {
			boolean isLibInput = false;
			boolean isInterpreter = false;

			/*
			 * check for legal input and input-type (library or program)&& is
			 * using short-circuit evaluation, hence -L will be checked without
			 * segfault
			 */

			// TODO change inargs handling w.r. to the new possible input
			if (args.length >= 2) {
				if (args[1].substring(0, 2).equals("-L"))
					isLibInput = true;
				if (args.length > 2 || (args.length == 2 && !isLibInput))
					isInterpreter = true;
			} else if (args.length == 1) {
				isLibInput = false;
			} else {
				System.out
						.println("Usage: Main <program-filename> [-L<library-filename>] [class.method] [args ...]");
				System.exit(0);
			}

			Lexer progLexer = new Lexer();
			Parser progParser = new Parser(false);
			List<Token> progTokens = new LinkedList<Token>();

			try {
				Node lib_ast = null;
				if (isLibInput || new File(DEFAULT_LIB_FILE).exists()) {
					Lexer libLexer = new Lexer();
					Parser libParser = new Parser(true);
					List<Token> libTokens = new LinkedList<Token>();

					String filename;

					if (isLibInput)
						filename = args[1].substring(2);
					else
						filename = DEFAULT_LIB_FILE;

					libLexer.process(filename, libTokens);
					lib_ast = libParser.process(libTokens);
				}
				progLexer.process(args[0], progTokens);
				Node ast = progParser.process(progTokens);
				// System.out.println(ast.accept(new PrettyPrint()));

				// "interpreter only" section
				if (isInterpreter) {
					int intargs_start = isLibInput ? 2 : 1;

					String int_args[] = Arrays.copyOfRange(args,
							intargs_start + 1, args.length);

					final String fullname = args[intargs_start];

					Object res;

					try {
						res = ast.accept(new interp.InterpreterVisitor(
								fullname, Arrays.asList(int_args)));
					} catch (LanguageException e) {
						System.err.println(e.getLine()
								+ ": interpreter error; "
								+ capitalize(e.getMessage()));
						return;
					}

					if (res instanceof Object[]) {
						Object[] arr = (Object[]) res;
						for (int i = 0; i < arr.length; i++) {
							System.out.println(arr[i]);
						}
					} else {
						System.out.println(res);
					}
				} else {
					// if not interpreter input - check semantics
					try {
						ast.accept(new ScopeFiller(lib_ast));
						ast.accept(new TypeAndRefChecker());
						/*
						 * System.out.println(program_scope.prettyPrint(
						 * isLibInput, lib_ast));
						 */
					} catch (LanguageException e) {
						System.err.println(e.getLine() + ": semantic error; "
								+ capitalize(e.getMessage()));
						return;
					}

					// then print IR-3AC
					try {
						TacVisitor codeGenVisitor = new TacVisitor();
						Object res = ast.accept(codeGenVisitor);
						System.out.println(res);
					} catch (LanguageException e) {
						System.err.println(e.getLine()
								+ ": 3AC generator error; "
								+ capitalize(e.getMessage()));
						return;
					}
				}
			} catch (Error e) {
				throw e;
			}
		} catch (Exception e) {
			System.err.println(e);
			e.printStackTrace();
		}
	}

	private static String capitalize(String input) {
		return Character.toUpperCase(input.charAt(0)) + input.substring(1);
	}
}